#include "GLWidget.h"

#include "gllib/Camera.h"
#include "gllib/Objects.h"

#include <QMouseEvent>
#include <QDebug>

GLWidget::GLWidget(QWidget * _parent): QGLWidget(_parent) {
  camera = 0;
  currentEvent = NONE;
  selectionBuffer = 0;
}

GLWidget::~GLWidget() {
  if(camera) delete camera;
  if(selectionBuffer) delete selectionBuffer;
}

void GLWidget::initializeGL() {
  camera = new Camera(width(), height());
  camera->SetDefaultPosition(1,1,3);

  glClearColor(0.25, 0.25, 0.25, 1.0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glShadeModel(GL_SMOOTH);
  glFrontFace(GL_CCW);

  GLfloat ambient[] = {0.0, 0.0, 0.0, 0.0};
  GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat position[] = {1.0, 1.0, 3.0, 1.0};

  glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, position);

  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);

  camera->Reset();
  glInitNames();
}

void GLWidget::resizeGL(int _w, int _h) {
  glViewport(0, 0, _w, _h);
  camera->SetDefaultCanvasSize(_w, _h);
}

void GLWidget::paintGL() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  camera->Draw();

  glDisable(GL_LIGHTING);

  drawBackground();
  drawGrid();
  drawAxis();

  glEnable(GL_LIGHTING);

  GLfloat position[] = {camera->GetPosition(0), camera->GetPosition(1), camera->GetPosition(2), 1.0};
  glLightfv(GL_LIGHT0, GL_POSITION, position);

  emit(drawObjects());

  glFlush();
}

void GLWidget::drawBackground() {
  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glBegin(GL_POLYGON);
  glColor4f(0.2, 0.1, 0.15, 1.0);
  glVertex2f(-1.0, -1.0);
  glVertex2f(1.0, -2.0);
  glColor4f(0.5, 0.5, 0.5, 1.0);
  glVertex2f(1.0, 1.0);
  glVertex2f(-1.0, 2.0);
  glEnd();

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopAttrib();
}

void GLWidget::drawGrid() {
  glBegin(GL_LINES);
  glColor3f(0.1, 0.1, 0.1);

  for(double z = -5.0; z <= 5.0; z += 0.2) {
    if(fabs(z) < 0.00001) glColor3f(1.0, 0.0, 0.0);
    glVertex3f(-5, 0, z);
    glVertex3f(5, 0, z);

    if(fabs(z) < 0.00001) glColor3f(0.0, 0.0, 1.0);
    glVertex3f(z, 0, -5);
    glVertex3f(z, 0, 5);

    if(fabs(z) < 0.00001) glColor3f(0.1, 0.1, 0.1);
  }

  glEnd();

  glBegin(GL_LINE_LOOP);
    glVertex3f(-5, 0, -5);
    glVertex3f(5, 0, -5);
    glVertex3f(5, 0, 5);
    glVertex3f(-5, 0, 5);
  glEnd();
}

void GLWidget::drawAxis() {
  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  camera->SetCanvasSize(50, 50);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  Vector3 pos(camera->GetPosition(0), camera->GetPosition(1), camera->GetPosition(2));
  Vector3 front(camera->GetFront(0), camera->GetFront(1), camera->GetFront(2));
  Vector3 up(camera->GetUp(0), camera->GetUp(1), camera->GetUp(2));

  Vector3 dir = pos - front;
  dir = dir.normalize() * 1.5;

  gluLookAt(dir.x, dir.y, dir.z, 0, 0, 0, up.x, up.y, up.z);

  glBegin(GL_LINES);
  glColor3f(1.0, 0.0, 0.0);
  glVertex3f(0.0, 0.0, 0.0);
  glVertex3f(1.0, 0.0, 0.0);

  glColor3f(0.0, 1.0, 0.0);
  glVertex3f(0.0, 0.0, 0.0);
  glVertex3f(0.0, 1.0, 0.0);

  glColor3f(0.0, 0.0, 1.0);
  glVertex3f(0.0, 0.0, 0.0);
  glVertex3f(0.0, 0.0, 1.0);
  glEnd();

  camera->SetCanvasSize(width(), height());
  glMatrixMode(GL_MODELVIEW);

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopAttrib();
}

void GLWidget::initSelection(int _x, int _y) {
  int viewport[4];

  if(!selectionBuffer) selectionBuffer = new unsigned int[1024];
  glSelectBuffer(1024, selectionBuffer);
  glRenderMode(GL_SELECT);

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  glGetIntegerv(GL_VIEWPORT, viewport);
  gluPickMatrix(_x, height() - _y, 5, 5, viewport);
  camera->Projection();
  glMatrixMode(GL_MODELVIEW);

  /* Draw objects */

  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  int hits = glRenderMode(GL_RENDER);

  if(hits > 0) processSelection(hits);
}

void GLWidget::processSelection(int _hits) {
  unsigned int * pointer = selectionBuffer;
  int result;

  while(_hits > 0) {
    unsigned int numberNames = *pointer;
    pointer += 3;

    if(numberNames > 0) {
      result = *pointer;
      break;
    }

    _hits -= 1;
  }

  currentEvent = SELECTION;
}

void GLWidget::selectionMove(const QPoint & _p) {
}

void GLWidget::mouseMoveEvent(QMouseEvent * _event) {
  switch(currentEvent) {
    case ZOOM:
      camera->Zoom(_event->x(), _event->y());
      break;
    case ROTATION:
      camera->SetUp(0.0,1.0,0.0);//Eixo Y.
      camera->Rotate(_event->x(), _event->y());
      break;
    case TRANSLATION:
      camera->Translate(_event->x(), _event->y());
      break;
    case SELECTION:
      selectionMove(_event->pos());
      break;
    case NONE:
      break;
  }

  update();
}

void GLWidget::mousePressEvent(QMouseEvent * _event) {
  currentEvent = NONE;
  if(_event->buttons() & Qt::LeftButton) {
    initSelection(_event->x(), _event->y());
    selectionPoint = _event->pos();
  }
  if(_event->buttons() & Qt::MidButton) currentEvent = ROTATION;
  if(_event->buttons() & Qt::RightButton) currentEvent = TRANSLATION;

  camera->SetLast(_event->x(), _event->y());
}

void GLWidget::mouseReleaseEvent(QMouseEvent * _event) {
  currentEvent = NONE;
}

void GLWidget::wheelEvent(QWheelEvent * _event) {
  camera->SetLast(0, 500);
  camera->Zoom(0, 500 + _event->delta() * 3);
  camera->SetLast(_event->x(), _event->y());
  update();
}
